<!DOCTYPE html>
<meta charset="utf-8">
<title>content-visibility and layout/paint containment</title>
<link rel="help" href="https://drafts.csswg.org/css-contain/#content-visibility">
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1765615">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/rendering-utils.js"></script>
<meta name="assert" content="content-visibility: auto and elements skipping their content change the used value of the contain property to turn on layout/paint containment, affecting absolute/fixed positioned descendants.">
<style>
  /* Selectors for content-visibility */
  #spacer_for_far_to_viewport {
      height: 300vh;
  }
  .content_visibility {
      /* Dynamic modification of content-visibility may change whether style
         containment is applied, which in turn may cause drastic invalidations
         (e.g. rebuilding counters). Make the test more robust by forcing
         style containment to always apply. */
      contain: style;
  }
  #visible {
      content-visibility: visible;
  }
  #hidden {
      content-visibility: hidden;
  }
  #auto_far {
      content-visibility: auto;
  }
  #auto_close {
      content-visibility: auto;
  }
  #visible_to_hidden {
      content-visibility: visible;
  }
  #hidden_to_visible {
      content-visibility: hidden;
  }
  #visible_to_auto {
      content-visibility: visible;
  }
  #auto_to_visible {
      content-visibility: auto;
  }

  /* Selectors for testing absolute/fixed positioning container blocks */
  #top_spacer {
      height: 100px;
      background: lightgray;
  }
  .absolute_pos {
      position: absolute;
      top: 42px;
  }
  .fixed_pos {
      position: fixed;
      top: 42px;
  }
</style>
<body>
  <div id="log"></div>

  <div id="top_spacer"></div>

  <div id="visible" class="content_visibility">
    <div class="absolute_pos"></div>
    <div class="fixed_pos"></div>
  </div>

  <div id="hidden" class="content_visibility">
    <div class="absolute_pos"></div>
    <div class="fixed_pos"></div>
  </div>

  <div id="auto_close" class="content_visibility">
    <div class="absolute_pos"></div>
    <div class="fixed_pos"></div>
  </div>

  <div id="visible_to_hidden" class="content_visibility">
    <div class="absolute_pos"></div>
    <div class="fixed_pos"></div>
  </div>

  <div id="hidden_to_visible" class="content_visibility">
    <div class="absolute_pos"></div>
    <div class="fixed_pos"></div>
  </div>

  <div id="visible_to_auto" class="content_visibility">
    <div class="absolute_pos"></div>
    <div class="fixed_pos"></div>
  </div>

  <div id="auto_to_visible" class="content_visibility">
    <div class="absolute_pos"></div>
    <div class="fixed_pos"></div>
  </div>

  <div id="spacer_for_far_to_viewport"></div>

  <div id="auto_far" class="content_visibility">
    <div class="absolute_pos"></div>
    <div class="fixed_pos"></div>
  </div>

  <script>
    function verifyContainmentFromAbsoluteFixedPositioning(id, applied) {
        // content-visibility: auto and elements skipping their content change
        // apply paint/layout containment, making them an absolute/fixed
        // positioning container blocks.
        // See contain-paint-dynamic-001.html / contain-layout-dynamic-001.html.

        let container = document.getElementById(id);
        let containerTop = container.getBoundingClientRect().top;

        let abs_top = container.getElementsByClassName("absolute_pos")[0]
            .getBoundingClientRect().top;
        assert_equals(abs_top > containerTop, applied, "absolute positioning containing block");

        let fixed_top = container.getElementsByClassName("fixed_pos")[0]
            .getBoundingClientRect().top;
        assert_equals(fixed_top > containerTop, applied, "fixed positioning containing block");
    }

    function setContentVisibility(id, value) {
        let container = document.getElementById(id);
        container.style.contentVisibility = value;
    }

    promise_test(async () => {
        verifyContainmentFromAbsoluteFixedPositioning("visible",
                                                      /*applied=*/false);
    }, "content-visibility: visible");

    promise_test(async () => {
        verifyContainmentFromAbsoluteFixedPositioning("hidden",
                                                      /*applied=*/true);
    }, "content-visibility: hidden");

    promise_test(async () => {
        await waitForAtLeastOneFrame();
        verifyContainmentFromAbsoluteFixedPositioning("auto_far",
                                                      /*applied=*/true);
    }, "content-visibility: auto (far from viewport)");

    promise_test(async () => {
        await waitForAtLeastOneFrame();
        verifyContainmentFromAbsoluteFixedPositioning("auto_close",
                                                      /*applied=*/true);
    }, "content-visibility: auto (close from viewport)");

    promise_test(async () => {
        setContentVisibility("visible_to_hidden", "hidden");
        verifyContainmentFromAbsoluteFixedPositioning("visible_to_hidden",
                                                      /*applied=*/true);
    }, "switching content-visibility from visible to hidden");

    promise_test(async () => {
        setContentVisibility("hidden_to_visible", "visible");
        verifyContainmentFromAbsoluteFixedPositioning("hidden_to_visible",
                                                      /*applied=*/false);
    }, "switching content-visibility from hidden to visible");

    promise_test(async () => {
        setContentVisibility("visible_to_auto", "auto");
        await waitForAtLeastOneFrame();
        verifyContainmentFromAbsoluteFixedPositioning("visible_to_auto",
                                                      /*applied=*/true);
    }, "switching content-visibility from visible to auto");

    promise_test(async () => {
        setContentVisibility("auto_to_visible", "visible");
        verifyContainmentFromAbsoluteFixedPositioning("auto_to_visible",
                                                      /*applied=*/false);
    }, "switching content-visibility from auto to visible");
  </script>
</body>
